我将参照第一个文件调整第二个文件，实现标题栏拖动和窗口缩放功能。以下是完整的修改后代码：

<script setup>
import { ref, onMounted, watch, onUnmounted } from "vue";
import * as echarts from "echarts";
import { useScaleStore } from "@/stores/scaleStore";

const props = defineProps({
  title: {
    type: String,
    default: "标题",
  },
  originalData: {
    type: Object,
    default: null,
  },
  waterDepth: {
    type: Number,
    default: 1000,
  },
  closeEvent: Function,
});

const closeClick = () => {
  props?.closeEvent();
};

let myChart = null;
const chartDom = ref(null);
const boxWrapRef = ref(null);
const headerRef = ref(null);
const isDragging = ref(false);
const startPos = ref({ x: 0, y: 0 });
const currentPos = ref({ x: 0, y: 0 });
const currentScale = ref(1);
const isResizing = ref(false);
const resizeDirection = ref(null);
const startSize = ref({ width: 600, height: 500 });
const currentSize = ref({ width: 600, height: 500 });

const scaleStore = useScaleStore();

watch(
  () => scaleStore.currentScale,
  (newScale) => {
    currentScale.value = newScale || 1;
  },
  { immediate: true }
);

const handleMouseDown = (e) => {
  // 检查是否是缩放区域
  if (e.target.classList.contains("resize-handle")) {
    isResizing.value = true;
    resizeDirection.value = e.target.dataset.direction;
    startSize.value = { ...currentSize.value };
    startPos.value = { x: e.clientX, y: e.clientY };

    e.stopPropagation();
    e.preventDefault();
    document.addEventListener("mousemove", handleResizeMove);
    document.addEventListener("mouseup", handleResizeEnd);
    return;
  }

  // 原有拖动逻辑 - 现在只在标题栏区域拖动
  if (!headerRef.value || !headerRef.value.contains(e.target)) return;

  const adjustedX = e.clientX / currentScale.value;
  const adjustedY = e.clientY / currentScale.value;

  isDragging.value = true;
  startPos.value = {
    x: adjustedX - currentPos.value.x,
    y: adjustedY - currentPos.value.y,
  };

  e.stopPropagation();
  e.preventDefault();

  document.addEventListener("mousemove", handleMouseMove);
  document.addEventListener("mouseup", handleMouseUp);
};

const handleMouseMove = (e) => {
  if (!isDragging.value) return;

  const adjustedX = e.clientX / currentScale.value;
  const adjustedY = e.clientY / currentScale.value;

  const newX = adjustedX - startPos.value.x;
  const newY = adjustedY - startPos.value.y;

  currentPos.value = { x: newX, y: newY };

  if (boxWrapRef.value) {
    boxWrapRef.value.style.left = `${newX}px`;
    boxWrapRef.value.style.top = `${newY}px`;
  }

  e.preventDefault();
};

const handleMouseUp = () => {
  isDragging.value = false;
  document.removeEventListener("mousemove", handleMouseMove);
  document.removeEventListener("mouseup", handleMouseUp);
};

const handleResizeMove = (e) => {
  if (!isResizing.value) return;

  const deltaX = (e.clientX - startPos.value.x) / currentScale.value;
  const deltaY = (e.clientY - startPos.value.y) / currentScale.value;

  const newSize = { ...currentSize.value };

  switch (resizeDirection.value) {
    case "e":
      newSize.width = Math.max(400, startSize.value.width + deltaX);
      break;
    case "s":
      newSize.height = Math.max(300, startSize.value.height + deltaY);
      break;
    case "se":
      newSize.width = Math.max(400, startSize.value.width + deltaX);
      newSize.height = Math.max(300, startSize.value.height + deltaY);
      break;
  }

  currentSize.value = newSize;
  updateChartSize();
  e.preventDefault();
};

const handleResizeEnd = () => {
  isResizing.value = false;
  resizeDirection.value = null;
  document.removeEventListener("mousemove", handleResizeMove);
  document.removeEventListener("mouseup", handleResizeEnd);
};

const updateChartSize = () => {
  if (myChart) {
    myChart.resize();
  }
};

// 处理温盐剖面数据
const processProfileData = (data) => {
  if (!data || !data.press || !data.temperature || !data.salinity) return null;

  const minLength = Math.min(
    data.press.length,
    data.temperature.length,
    data.salinity.length
  );

  const temperatureData = [];
  const salinityData = [];

  for (let i = 0; i < minLength; i++) {
    if (data.press[i] !== null && data.temperature[i] !== null) {
      temperatureData.push([data.temperature[i], data.press[i]]);
    }
    if (data.press[i] !== null && data.salinity[i] !== null) {
      salinityData.push([data.salinity[i], data.press[i]]);
    }
  }

  return {
    temperature: temperatureData,
    salinity: salinityData,
  };
};

// 初始化ECharts图表
onMounted(async () => {
  await initChart();
  // 初始化位置
  if (boxWrapRef.value) {
    const rect = boxWrapRef.value.getBoundingClientRect();
    currentPos.value = { x: rect.left, y: rect.top };
  }
  window.addEventListener("resize", handleResize);
});

// 监听数据变化
watch(
  () => props.originalData,
  () => {
    updateChart();
  },
  { deep: true }
);

const initChart = async () => {
  await new Promise((resolve) => requestAnimationFrame(resolve));
  if (!chartDom.value) return;
  myChart = echarts.init(chartDom.value);
  updateChart();
};

const handleResize = () => {
  myChart?.resize();
};

const updateChart = () => {
  if (!myChart) return;

  const profileData = processProfileData(props.originalData);
  const hasProfileData = profileData !== null;

  const calcProfileRange = (profile) => {
    if (!profile || profile.length === 0) return { min: 0, max: 1 };
    const values = profile.map((item) => item[0]);
    const min = Math.min(...values);
    const max = Math.max(...values);
    const range = max - min;
    return {
      min: min - range * 0.1,
      max: max + range * 0.1,
    };
  };

  const tempRange = calcProfileRange(profileData?.temperature);
  const saltRange = calcProfileRange(profileData?.salinity);

  const calcGradient = (profile, startDepth, endDepth) => {
    if (!profile || profile.length === 0) return 0;

    const startIndex = profile.findIndex((item) => item[1] >= startDepth);
    const endIndex = profile.findLastIndex((item) => item[1] <= endDepth);

    if (startIndex === -1 || endIndex === -1 || startIndex >= endIndex)
      return 0;

    const startValue = profile[startIndex][0];
    const endValue = profile[endIndex][0];
    const depthDiff = profile[endIndex][1] - profile[startIndex][1];

    return depthDiff !== 0 ? Math.abs((endValue - startValue) / depthDiff) : 0;
  };

  const tempGradient = hasProfileData
    ? calcGradient(
        profileData.temperature,
        props.originalData?.temp_shang,
        props.originalData?.temp_xia
      )
    : 0;
  const saltGradient = hasProfileData
    ? calcGradient(
        profileData.salinity,
        props.originalData?.salt_shang,
        props.originalData?.salt_xia
      )
    : 0;

  // 计算标签位置避免重叠 - 温度标签在左侧，盐度标签在右侧
  const tempLabelPositions = calculateLabelPositions(
    props.originalData?.temp_shang,
    props.originalData?.temp_xia,
    props.waterDepth,
    "left"
  );
  const saltLabelPositions = calculateLabelPositions(
    props.originalData?.salt_shang,
    props.originalData?.salt_xia,
    props.waterDepth,
    "right"
  );

  const option = {
    title: {
      text: "温盐跃层及剖面",
      left: "center",
      textStyle: {
        color: "#38e1ff",
        fontSize: 14,
      },
      top: 10,
    },
    tooltip: {
      trigger: "axis",
      axisPointer: { type: "shadow" },
      formatter: (params) => {
        let result = "";
        params.forEach((param) => {
          const seriesName = param.seriesName;
          if (seriesName === "温度跃层") {
            result += `温度跃层:<br/>
                      上界: ${props.originalData?.temp_shang}m<br/>
                      下界: ${props.originalData?.temp_xia}m<br/>
                      厚度: ${(
                        props.originalData?.temp_xia -
                        props.originalData?.temp_shang
                      ).toFixed(2)}m<br/>
                      强度: ${tempGradient.toFixed(4)}°C/m<br/>`;
          } else if (seriesName === "盐度跃层") {
            result += `盐度跃层:<br/>
                      上界: ${props.originalData?.salt_shang}m<br/>
                      下界: ${props.originalData?.salt_xia}m<br/>
                      厚度: ${(
                        props.originalData?.salt_xia -
                        props.originalData?.salt_shang
                      ).toFixed(2)}m<br/>
                      强度: ${saltGradient.toFixed(4)}PSU/m<br/>`;
          } else if (seriesName.includes("剖面")) {
            result += `${seriesName}: 值=${param.value[0].toFixed(2)}, 深度=${
              param.value[1]
            }m<br/>`;
          }
        });
        return result;
      },
    },
    legend: {
      data: [
        "温度跃层",
        "盐度跃层",
        ...(hasProfileData ? ["温度剖面", "盐度剖面"] : []),
      ],
      textStyle: {
        color: "#fff",
        fontSize: 14,
      },
      top: 40,
      right: 10,
      itemWidth: 12,
      itemHeight: 8,
      itemGap: 5,
    },
    grid: [
      {
        left: "12%",
        right: "52%",
        bottom: "15%",
        top: "25%",
      },
      {
        left: "52%",
        right: "12%",
        bottom: "15%",
        top: "25%",
      },
    ],
    xAxis: [
      {
        gridIndex: 0,
        type: "category",
        data: ["温度跃层"],
        axisLabel: {
          color: "#FFFFFF",
        },
        axisLine: {
          lineStyle: {
            color: "#fff",
            width: 1,
          },
        },
      },
      {
        gridIndex: 1,
        type: "category",
        data: ["盐度跃层"],
        axisLabel: {
          color: "#FFFFFF",
        },
        axisLine: {
          lineStyle: {
            color: "#fff",
            width: 1,
          },
        },
      },
      {
        gridIndex: 0,
        type: "value",
        name: "温度 (°C)",
        nameLocation: "middle",
        nameGap: 25,
        min: tempRange.min,
        max: tempRange.max,
        axisLabel: {
          color: "#FFA500",
        },
        axisLine: {
          lineStyle: {
            color: "#FFA500",
            width: 1,
          },
        },
      },
      {
        gridIndex: 1,
        type: "value",
        name: "盐度 (PSU)",
        nameLocation: "middle",
        nameGap: 25,
        min: saltRange.min,
        max: saltRange.max,
        axisLabel: {
          color: "#1E90FF",
        },
        axisLine: {
          lineStyle: {
            color: "#1E90FF",
            width: 1,
          },
        },
      },
    ],
    yAxis: [
      {
        gridIndex: 0,
        type: "value",
        name: "深度 (m)",
        min: 0,
        max: props.waterDepth,
        inverse: true,
        axisLabel: {
          color: "#FFFFFF",
          margin: 2,
        },
        axisLine: {
          lineStyle: {
            color: "#fff",
            width: 1,
          },
        },
        nameTextStyle: {
          padding: [0, 0, 0, -20],
        },
      },
      {
        gridIndex: 1,
        type: "value",
        name: "深度 (m)",
        min: 0,
        max: props.waterDepth,
        inverse: true,
        axisLabel: {
          color: "#FFFFFF",
          margin: 2,
        },
        axisLine: {
          lineStyle: {
            color: "#fff",
            width: 1,
          },
        },
        nameTextStyle: {
          padding: [0, 0, 0, -20],
        },
      },
    ],
    series: [
      // 温度跃层柱状图 - 标签在左侧
      {
        name: "温度跃层",
        type: "custom",
        renderItem: (params, api) => {
          const startDepth = props.originalData?.temp_shang || 0;
          const endDepth = props.originalData?.temp_xia || 0;
          const thickness = endDepth - startDepth;

          if (thickness <= 0) return;

          const startPoint = api.coord([api.value(0), startDepth]);
          const endPoint = api.coord([api.value(1), endDepth]);
          const width = api.size([1, 0])[0] * 0.3;

          return {
            type: "rect",
            shape: {
              x: startPoint[0] - width / 2,
              y: startPoint[1],
              width: width,
              height: endPoint[1] - startPoint[1],
            },
            style: api.style({
              fill: new echarts.graphic.LinearGradient(0, 0, 0, 1, [
                { offset: 0, color: "rgba(255, 165, 0, 0.7)" },
                { offset: 1, color: "rgba(255, 0, 0, 0.7)" },
              ]),
            }),
          };
        },
        data: [1],
        xAxisIndex: 0,
        yAxisIndex: 0,
        markLine: {
          silent: true,
          symbol: "none",
          lineStyle: {
            color: "#FF0000",
            width: 1,
            type: "dashed",
          },
          data: [
            {
              yAxis: props.originalData?.temp_shang || 0,
              label: {
                show: true,
                position: tempLabelPositions.upper,
                formatter: "上界: {c}m",
                color: "#FFA500",
                fontSize: 10,
                padding: [
                  2,
                  0,
                  0,
                  tempLabelPositions.upper === "end" ? -30 : 0,
                ],
              },
            },
            {
              yAxis: props.originalData?.temp_xia || 0,
              label: {
                show: true,
                position: tempLabelPositions.lower,
                formatter: "下界: {c}m",
                color: "#FF0000",
                fontSize: 10,
                padding: [
                  0,
                  0,
                  2,
                  tempLabelPositions.lower === "end" ? -30 : 0,
                ],
              },
            },
          ],
        },
      },
      // 盐度跃层柱状图 - 标签在右侧
      {
        name: "盐度跃层",
        type: "custom",
        renderItem: (params, api) => {
          const startDepth = props.originalData?.salt_shang || 0;
          const endDepth = props.originalData?.salt_xia || 0;
          const thickness = endDepth - startDepth;

          if (thickness <= 0) return;

          const startPoint = api.coord([api.value(0), startDepth]);
          const endPoint = api.coord([api.value(1), endDepth]);
          const width = api.size([1, 0])[0] * 0.3;

          return {
            type: "rect",
            shape: {
              x: startPoint[0] - width / 2,
              y: startPoint[1],
              width: width,
              height: endPoint[1] - startPoint[1],
            },
            style: api.style({
              fill: new echarts.graphic.LinearGradient(0, 0, 0, 1, [
                { offset: 0, color: "rgba(30, 144, 255, 0.7)" },
                { offset: 1, color: "rgba(0, 255, 0, 0.7)" },
              ]),
            }),
          };
        },
        data: [1],
        xAxisIndex: 1,
        yAxisIndex: 1,
        markLine: {
          silent: true,
          symbol: "none",
          lineStyle: {
            color: "#00FF00",
            width: 1,
            type: "dashed",
          },
          data: [
            {
              yAxis: props.originalData?.salt_shang || 0,
              label: {
                show: true,
                position: "end",
                formatter: "上界: {c}m",
                color: "#1E90FF",
                fontSize: 10,
                padding: [2, 0, 0, 0],
              },
            },
            {
              yAxis: props.originalData?.salt_xia || 0,
              label: {
                show: true,
                position: "end",
                formatter: "下界: {c}m",
                color: "#00FF00",
                fontSize: 10,
                padding: [0, 0, 2, 0],
              },
            },
          ],
        },
      },
      // 温度剖面数据
      ...(hasProfileData
        ? [
            {
              name: "温度剖面",
              type: "line",
              xAxisIndex: 2,
              yAxisIndex: 0,
              symbol: "none",
              lineStyle: {
                color: "#FFA500",
                width: 2,
              },
              data: profileData.temperature,
              z: 10,
            },
          ]
        : []),
      // 盐度剖面数据
      ...(hasProfileData
        ? [
            {
              name: "盐度剖面",
              type: "line",
              xAxisIndex: 3,
              yAxisIndex: 1,
              symbol: "none",
              lineStyle: {
                color: "#1E90FF",
                width: 2,
              },
              data: profileData.salinity,
              z: 10,
            },
          ]
        : []),
    ],
  };

  myChart.setOption(option, true);
};

// 计算标签位置避免重叠
const calculateLabelPositions = (
  upperBound,
  lowerBound,
  maxDepth,
  side = "left"
) => {
  if (!upperBound || !lowerBound) return { upper: "start", lower: "start" };

  const threshold = maxDepth * 0.15;

  if (Math.abs(upperBound - lowerBound) < threshold) {
    return {
      upper: side === "left" ? "start" : "end",
      lower: side === "left" ? "end" : "start",
    };
  }

  return {
    upper: side === "left" ? "start" : "end",
    lower: side === "left" ? "start" : "end",
  };
};

onUnmounted(() => {
  window.removeEventListener("resize", handleResize);
  myChart?.dispose();
  document.removeEventListener("mousemove", handleMouseMove);
  document.removeEventListener("mouseup", handleMouseUp);
  document.removeEventListener("mousemove", handleResizeMove);
  document.removeEventListener("mouseup", handleResizeEnd);
});
</script>

<template>
  <div
    class="popup-container"
    :style="{
      left: `${currentPos.x}px`,
      top: `${currentPos.y}px`,
      width: `${currentSize.width}px`,
      height: `${currentSize.height}px`,
    }"
  >
    <div class="pine"></div>
    <div class="box-wrap" ref="boxWrapRef">
      <!-- 标题栏区域 -->
      <div class="header-area" ref="headerRef" @mousedown="handleMouseDown">
        <div class="title-text">{{ title }}</div>
        <div class="close" @click="closeClick">×</div>
      </div>

      <!-- 图表区域 -->
      <div
        v-if="originalData != null"
        ref="chartDom"
        class="chart-container"
      ></div>
      <div v-else class="area">
        <div class="area-title fontColor">
          <template v-if="props.title">{{ props.title }}</template>
          <slot v-else name="title"></slot>
        </div>
      </div>

      <!-- 缩放控制手柄 -->
      <div
        class="resize-handle resize-e"
        data-direction="e"
        @mousedown="handleMouseDown"
      ></div>
      <div
        class="resize-handle resize-s"
        data-direction="s"
        @mousedown="handleMouseDown"
      ></div>
      <div
        class="resize-handle resize-se"
        data-direction="se"
        @mousedown="handleMouseDown"
      ></div>
    </div>
  </div>
</template>

<style lang="less" scoped>
.popup-container {
  position: fixed;
  z-index: 9999;
  cursor: default;
  min-width: 400px;
  min-height: 300px;
}

.box-wrap {
  position: relative;
  width: 100%;
  height: 100%;
  border-radius: 50px 0px 50px 0px;
  border: 1px solid #38e1ff;
  background-color: rgba(30, 80, 120, 0.7);
  box-shadow: 0 0 10px 2px #29baf1;
  animation: slide 2s;
  padding: 15px;
  box-sizing: border-box;
  user-select: none;
  overflow: hidden;
}

/* 标题栏样式 */
.header-area {
  position: relative;
  height: 40px;
  background: linear-gradient(
    to right,
    rgba(20, 60, 100, 0.9),
    rgba(30, 80, 120, 0.9)
  );
  border-bottom: 1px solid #38e1ff;
  display: flex;
  align-items: center;
  justify-content: space-between;
  padding: 0 20px;
  cursor: grab;

  &:active {
    cursor: grabbing;
  }
}

.title-text {
  color: #38e1ff;
  font-size: 18px;
  font-weight: bold;
  text-shadow: 0 0 5px rgba(56, 225, 255, 0.7);
}

.close {
  color: #fff;
  text-shadow: 2px 2px 2px #022122;
  cursor: pointer;
  font-size: 20px;
  transition: all 0.2s;

  &:hover {
    color: #ff6b6b;
    transform: scale(1.1);
  }
}

/* 图表容器 */
.chart-container {
  width: 100%;
  height: calc(100% - 40px); /* 减去标题栏高度 */
  margin: 0 auto;
}

.box-wrap .area {
  position: absolute;
  top: 20px;
  right: 0;
  width: 95%;
  height: 30px;
  background-image: linear-gradient(to left, #4cdef9, #4cdef96b);
  border-radius: 30px 0px 0px 0px;
  animation: area 1s;
}

.area .area-title {
  text-align: center;
  line-height: 30px;
}

.textColor {
  font-size: 14px;
  font-weight: 600;
  color: #ffffff;
  text-shadow: 1px 1px 5px #002520d2;
  animation: fontColor 1s;
}

.yellowColor {
  font-size: 14px;
  font-weight: 600;
  color: #f09e28;
  text-shadow: 1px 1px 5px #002520d2;
  animation: fontColor 1s;
}

.fontColor {
  font-size: 16px;
  font-weight: 800;
  color: #ffffff;
  text-shadow: 1px 1px 5px #002520d2;
  animation: fontColor 1s;
}

/* 缩放控制手柄样式 */
.resize-handle {
  position: absolute;
  background: transparent;
  z-index: 10;
}

.resize-e {
  cursor: e-resize;
  width: 10px;
  height: 100%;
  right: 0;
  top: 0;
}

.resize-s {
  cursor: s-resize;
  width: 100%;
  height: 10px;
  bottom: 0;
  left: 0;
}

.resize-se {
  cursor: se-resize;
  width: 15px;
  height: 15px;
  right: 0;
  bottom: 0;
  background: #38e1ff;
  opacity: 0;
  transition: opacity 0.2s;
}

.box-wrap:hover .resize-se {
  opacity: 0.8;
}

@keyframes fontColor {
  0% {
    color: #ffffff00;
    text-shadow: 1px 1px 5px #00252000;
  }

  40% {
    color: #ffffff00;
    text-shadow: 1px 1px 5px #00252000;
  }

  100% {
    color: #ffffff;
    text-shadow: 1px 1px 5px #002520d2;
  }
}

@keyframes slide {
  0% {
    border: 1px solid #38e1ff00;
    background-color: #38e1ff00;
    box-shadow: 0 0 10px 2px #29baf100;
  }

  100% {
    border: 1px solid #38e1ff;
    background-color: rgba(30, 80, 120, 0.7);
    box-shadow: 0 0 10px 2px #29baf1;
  }
}

@keyframes area {
  0% {
    width: 0%;
  }

  25% {
    width: 0%;
  }

  100% {
    width: 95%;
  }
}
</style>
